package com.alex.utils.html;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;

public class Form {

	public static final int HTTP_METHOD_GET = 0;

	public static final int HTTP_METHOD_POST = 1;

	private String action = "";

	private int method = HTTP_METHOD_GET;

	private Collection<Control> controls = new ArrayList<Control>();

	private Hashtable<String, Control> hashControls = new Hashtable<String, Control>();

	private Form() {
	}

	public void addControl(Control control) {
		controls.add(control);
		hashControls.put(control.getName(), control);
	}

	public Control getControlByName(String name) {
		return hashControls.get(name);
	}

	public static Form parseForm(String html) {

		Form form = new Form();

		Map<String, RadioInput> radioButtons = new HashMap<String, RadioInput>();

		TextArea currenTextArea = null;
		Select currenSelect = null;
		int compositeTagBegin = 0;

		int p1 = 0;
		int p2 = 0;
		for (;;) {

			if (p2 < 0)
				break;

			p1 = html.indexOf("<", p2);
			if (p1 < 0)
				break;

			p2 = html.indexOf(">", p1);
			if (p2 < 0)
				break;

			String tag = html.substring(p1 + 1, p2);
			String tagName;

			int p3 = tag.indexOf(' ');
			if (p3 > 0)
				tagName = tag.substring(0, p3);
			else
				tagName = tag;

			if (tagName.equalsIgnoreCase("form") && p3 > 0) {

				Map<String, String> atributes = getAtributes(tag, p3);
				form.action = atributes.get("action");
				String method = atributes.get("method");

				if (method != null && method.equalsIgnoreCase("post"))
					form.method = HTTP_METHOD_GET;
				else
					form.method = HTTP_METHOD_POST;

				if (form.action == null)
					form.action = "";

			} else if (tagName.equalsIgnoreCase("input") && p3 > 0) {

				Map<String, String> atributes = getAtributes(tag, p3);
				String type = atributes.get("type");
				String name = atributes.get("name");
				if (name == null)
					continue;
				if (type != null)
					type = type.toLowerCase();
				if (type == null || type.equals("text")) {

					form.addControl(new TextInput(name, atributes.get("value")));

				} else if (type.equals("hidden")) {

					form.addControl(new HiddenInput(name, atributes.get("value")));

				} else if (type.equals("checkbox")) {

					form.addControl(new CheckBox(name, atributes.get("value"), atributes.get("checked") != null));

				} else if (type.equals("button")) {

					form.addControl(new Button(name, atributes.get("value")));

				} else if (type.equals("password")) {

					form.addControl(new PasswordInput(name, atributes.get("value")));

				} else if (type.equals("submit")) {

					form.addControl(new SubmitButton(name, atributes.get("value")));

				} else if (type.equals("radio")) {

					RadioInput radioInput = radioButtons.get(name);
					if (radioInput == null) {
						radioInput = new RadioInput(name);
						radioInput.addOption(atributes.get("value"), true);
						form.addControl(radioInput);
						radioButtons.put(name, radioInput);
					} else
						radioInput.addOption(atributes.get("value"), atributes.get("checked") != null);
				} else if (type.equals("upload")) {

				}

			} else if (tagName.equalsIgnoreCase("select") && p3 > 0) {

				Map<String, String> atributes = getAtributes(tag, p3);
				String name = atributes.get("name");
				if (name == null)
					continue;
				currenSelect = new Select(name);
				compositeTagBegin = p2 + 1;

			} else if (currenSelect != null && tagName.equalsIgnoreCase("/select")) {

				form.addControl(currenSelect);
				currenSelect = null;

			} else if (currenSelect != null && tagName.equalsIgnoreCase("option") && p3 > 0) {

				Map<String, String> atributes = getAtributes(tag, p3);
				String value = atributes.get("value");
				if (value == null)
					continue;

				currenSelect.addOption(value, atributes.get("selected") != null);

			} else if (tagName.equalsIgnoreCase("textarea") && p3 > 0) {

				Map<String, String> atributes = getAtributes(tag, p3);
				String name = atributes.get("name");
				if (name == null)
					continue;
				currenTextArea = new TextArea(name, "");
				compositeTagBegin = p2 + 1;

			} else if (currenTextArea != null && tagName.equalsIgnoreCase("/textarea")) {

				String value = html.substring(compositeTagBegin, p1);
				currenTextArea.setValue(value);
				form.addControl(currenTextArea);
				currenTextArea = null;
			}
		}
		return form;
	}

	private static Map<String, String> getAtributes(String tag, int tagNameEnd) {

		Map<String, String> atributes = new HashMap<String, String>();

		int p1 = tagNameEnd + 1;
		int len = tag.length();
		int tokenBegin = -1;

		String currentParam = null;
		boolean value = false;
		boolean end = false;

		for (;;) {

			if (end)
				break;

			char c;
			if (p1 != len) {
				c = tag.charAt(p1);
			} else {
				c = ' ';
				end = true;
			}

			if (tokenBegin < 0 && c == '\"') {

				int p2 = tag.indexOf('\"', p1 + 1);
				if (p2 < 0)
					break;
				String token = tag.substring(p1 + 1, p2);

				if (value && currentParam != null)
					atributes.put(currentParam, token);
				currentParam = null;
				value = false;

				p1 = p2;

			} else if (tokenBegin < 0 && c == '\'') {

				int p2 = tag.indexOf('\'', p1 + 1);
				if (p2 < 0)
					break;
				String token = tag.substring(p1 + 1, p2);

				if (value && currentParam != null)
					atributes.put(currentParam, token);
				currentParam = null;
				value = false;

				p1 = p2;

			} else if (Character.isWhitespace(c) || c == '=') {

				if (c == '=' && value) {
					p1++;
					continue;
				}

				if (tokenBegin >= 0) {

					String token = tag.substring(tokenBegin, p1);
					tokenBegin = -1;

					if (value) {

						if (currentParam != null) {
							atributes.put(currentParam, token);
							currentParam = null;
							value = false;
						}

					} else if (currentParam != null) {

						atributes.put(currentParam, "");
						currentParam = token;

					} else
						currentParam = token;
				}

				if (c == '=')
					value = true;

			} else if (tokenBegin < 0)
				tokenBegin = p1;

			p1++;
		}

		if (currentParam != null)
			atributes.put(currentParam, "");

		return atributes;
	}

	public Map<String, String> getRequestParams(FormFiller formFiller) {
		Map<String, String> params = new HashMap<String, String>();
		for (Control control : controls) {
			if (control instanceof HiddenInput || formFiller.fillControl(control)) {
				String value = control.getValue();
				if (value != null)
					params.put(control.getName(), value);
			}
		}
		return params;
	}

	public void getRequestParams(FormFiller formFiller, Collection<NameValuePair> pairs) {
		for (Control control : controls) {
			if (control instanceof HiddenInput || formFiller == null || formFiller.fillControl(control)) {
				String value = control.getValue();
				if (value != null)
					pairs.add(new BasicNameValuePair(control.getName(), value));

			}
		}
	}

	public String getAction() {
		return action;
	}

	public void setAction(String action) {
		this.action = action;
	}

	public int getMethod() {
		return method;
	}

	public void setMethod(int method) {
		this.method = method;
	}
}
